﻿using MvCamCtrl.NET;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using VM.PlatformSDKCS;

namespace CarPCBATest.Procedure
{
    internal class CameraTool
    {
        private static bool open = false;
        private static bool getImage = false;
        private static MyCamera.cbOutputExdelegate ImageCallback;
        private static MyCamera device = new MyCamera();
        private static List<ImageBaseData> imageBaseDatas = new List<ImageBaseData>();

        public static void Open()
        {
            if (open)
            {
                return;
            }
            int nRet = MyCamera.MV_OK;

            // ch:枚举设备 | en:Enum device
            MyCamera.MV_CC_DEVICE_INFO_LIST stDevList = new MyCamera.MV_CC_DEVICE_INFO_LIST();
            nRet = MyCamera.MV_CC_EnumDevices_NET(MyCamera.MV_GIGE_DEVICE | MyCamera.MV_USB_DEVICE, ref stDevList);
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("Camera Enum device failed:{0:x8}");
            }
            Console.WriteLine("Enum device count : " + Convert.ToString(stDevList.nDeviceNum));
            if (1 != stDevList.nDeviceNum)
            {
                throw new Exception($"发现[{stDevList.nDeviceNum}]个相机");
            }

            MyCamera.MV_CC_DEVICE_INFO stDevInfo;                            // 通用设备信息

            // ch:打印设备信息 en:Print device info
            for (Int32 i = 0; i < stDevList.nDeviceNum; i++)
            {
                stDevInfo = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(stDevList.pDeviceInfo[i], typeof(MyCamera.MV_CC_DEVICE_INFO));

                if (MyCamera.MV_GIGE_DEVICE == stDevInfo.nTLayerType)
                {
                    MyCamera.MV_GIGE_DEVICE_INFO stGigEDeviceInfo = (MyCamera.MV_GIGE_DEVICE_INFO)MyCamera.ByteToStruct(stDevInfo.SpecialInfo.stGigEInfo, typeof(MyCamera.MV_GIGE_DEVICE_INFO));
                    uint nIp1 = ((stGigEDeviceInfo.nCurrentIp & 0xff000000) >> 24);
                    uint nIp2 = ((stGigEDeviceInfo.nCurrentIp & 0x00ff0000) >> 16);
                    uint nIp3 = ((stGigEDeviceInfo.nCurrentIp & 0x0000ff00) >> 8);
                    uint nIp4 = (stGigEDeviceInfo.nCurrentIp & 0x000000ff);
                    Console.WriteLine("[device " + i.ToString() + "]:");
                    Console.WriteLine("DevIP:" + nIp1 + "." + nIp2 + "." + nIp3 + "." + nIp4);
                    Console.WriteLine("UserDefineName:" + stGigEDeviceInfo.chUserDefinedName + "\n");
                }
                else if (MyCamera.MV_USB_DEVICE == stDevInfo.nTLayerType)
                {
                    MyCamera.MV_USB3_DEVICE_INFO stUsb3DeviceInfo = (MyCamera.MV_USB3_DEVICE_INFO)MyCamera.ByteToStruct(stDevInfo.SpecialInfo.stUsb3VInfo, typeof(MyCamera.MV_USB3_DEVICE_INFO));
                    Console.WriteLine("[device " + i.ToString() + "]:");
                    Console.WriteLine("SerialNumber:" + stUsb3DeviceInfo.chSerialNumber);
                    Console.WriteLine("UserDefineName:" + stUsb3DeviceInfo.chUserDefinedName + "\n");
                }
            }
            Int32 nDevIndex = 0;

            stDevInfo = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(stDevList.pDeviceInfo[nDevIndex], typeof(MyCamera.MV_CC_DEVICE_INFO));

            // ch:创建设备 | en:Create device
            nRet = device.MV_CC_CreateDevice_NET(ref stDevInfo);
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("Create device failed");
            }

            // ch:打开设备 | en:Open device
            nRet = device.MV_CC_OpenDevice_NET();
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("Open device failed");
            }

            // ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package
            // size(It only works for the GigE camera)
            if (stDevInfo.nTLayerType == MyCamera.MV_GIGE_DEVICE)
            {
                int nPacketSize = device.MV_CC_GetOptimalPacketSize_NET();
                if (nPacketSize > 0)
                {
                    nRet = device.MV_CC_SetIntValue_NET("GevSCPSPacketSize", (uint)nPacketSize);
                    if (nRet != MyCamera.MV_OK)
                    {
                        throw new Exception("Warning: Set Packet Size failed ");
                    }
                }
                else
                {
                    throw new Exception("Warning: Get Packet Size failed");
                }
            }

            // ch:设置触发模式为off || en:set trigger mode as off
            nRet = device.MV_CC_SetEnumValue_NET("TriggerMode", 0);
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("Set TriggerMode failed");
            }

            // ch:注册回调函数 | en:Register image callback
            ImageCallback = new MyCamera.cbOutputExdelegate(ImageCallbackFunc);
            nRet = device.MV_CC_RegisterImageCallBackEx_NET(ImageCallback, IntPtr.Zero);
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("Register image callback failed!");
            }
            open = true;
        }

        private static void ImageCallbackFunc(IntPtr pData, ref MyCamera.MV_FRAME_OUT_INFO_EX pFrameInfo, IntPtr pUser)
        {
            Console.WriteLine("Get one frame: Width[" + Convert.ToString(pFrameInfo.nWidth) + "] , Height[" + Convert.ToString(pFrameInfo.nHeight)
                                + "] , FrameNum[" + Convert.ToString(pFrameInfo.nFrameNum) + "]" + "addr" + pData);

            imageBaseDatas.Add(CCDToImageBaseData(pData, pFrameInfo));
            getImage = true;
        }

        public static void Close()
        {
            if (!open)
            {
                return;
            }
            // ch:关闭设备 | en:Close device
            int nRet = device.MV_CC_CloseDevice_NET();
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("Close device failed");
            }

            //// ch:销毁设备 | en:Destroy device
            nRet = device.MV_CC_DestroyDevice_NET();
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("Destroy device failed");
            }
            open = false;
        }

        /// <summary>
        /// 获取
        /// </summary>
        /// <param name="elapsedMilliseconds"> </param>
        /// <returns> </returns>
        public static List<ImageBaseData> GetLatestImages()
        {
            return imageBaseDatas;
        }

        public static void StartGrab()
        {
            imageBaseDatas.Clear();
            // ch:开启抓图 || en: start grab image
            int nRet = device.MV_CC_StartGrabbing_NET();
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("Start grabbing failed:{0:x8}");
            }
        }

        public static ImageBaseData GetCurrentImage()
        {
            imageBaseDatas.Clear();
            getImage = false;
            StartGrab();
            bool timeOut = false;
            Task.Run(() =>
            {
                Thread.Sleep(2000);
                timeOut = true;
            });
            while (!getImage)
            {
                if (timeOut)
                    break;
            }

            StopGrab();
            if (timeOut)
            {
                throw new Exception("采图执行异常0");
            }
            if (imageBaseDatas.Count < 1)
            {
                throw new Exception("采图执行异常1");
            }
            return imageBaseDatas[0];
        }

        public static void StopGrab()
        {
            // ch:停止抓图 | en:Stop grabbing
            int nRet = device.MV_CC_StopGrabbing_NET();
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("Stop grabbing failed");
            }
        }

        private static ImageBaseData CCDToImageBaseData(IntPtr pdata, MyCamera.MV_FRAME_OUT_INFO_EX frame)
        {
            ImageBaseData imageBaseData = new ImageBaseData();
            imageBaseData.Width = frame.nWidth;
            imageBaseData.Height = frame.nHeight;
            imageBaseData.DataLen = frame.nFrameLen;
            if (frame.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8)
            {
                imageBaseData.Pixelformat = (int)VMPixelFormat.VM_PIXEL_MONO_08;
            }
            else if (frame.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed)
            {
                imageBaseData.Pixelformat = (int)VMPixelFormat.VM_PIXEL_RGB24_C3;
            }
            else
            {
                imageBaseData.Pixelformat = (int)VMPixelFormat.VM_PIXEL_RGB24_C3;
            }
            imageBaseData.ImageData = new byte[frame.nFrameLen];
            Marshal.Copy(pdata, imageBaseData.ImageData, 0, (int)frame.nFrameLen);
            return imageBaseData;
        }
    }
}